//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	SyncDictionary.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

#define USE_FAST_SYNCMAP

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;

namespace Smart
{
	/// <summary>
	/// Synchronized map
	/// </summary>
	[Serializable]
	[DebuggerStepThrough]
	public class SyncDictionary<TKeyType, TValueType> : IDictionary<TKeyType, TValueType>, ISimpleDictionary
	{
		public Dictionary<TKeyType, TValueType> Storage;

		[NonSerialized]
		private readonly ReaderWriterLockSlim _sync = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);

		public SyncDictionary()
		{
			Storage = new Dictionary<TKeyType, TValueType>();
		}

		public SyncDictionary(IEqualityComparer<TKeyType> equalityComparer)
		{
			Storage = new Dictionary<TKeyType, TValueType>(equalityComparer);
		}

		public TValueType this[TKeyType key, Func<TKeyType, TValueType> loader]
		{
			get
			{
				TValueType res;

				_sync.EnterUpgradeableReadLock();

				try
				{
					if (!Storage.TryGetValue(key, out res))
					{
						_sync.EnterWriteLock();

						try
						{
							Storage[key] = res = loader(key);

						}
						finally
						{
							_sync.ExitWriteLock();
						}
					}
				}
				finally
				{
					_sync.ExitUpgradeableReadLock();
				}

				return res;
			}
		}

		#region IDictionary<KeyType,ValueType> Members

		public void Add(TKeyType key, TValueType value)
		{
			_sync.EnterWriteLock();

			try
			{
				Storage.Add(key, value);
			}
			finally
			{
				_sync.ExitWriteLock();
			}
		}

		public bool ContainsKey(TKeyType key)
		{
			bool Res = false;

			_sync.EnterReadLock();

			try
			{
				Res = Storage.ContainsKey(key);
			}
			finally
			{
				_sync.ExitReadLock();
			}

			return Res;
		}

		public ICollection<TKeyType> Keys
		{
			get
			{
				ICollection<TKeyType> res = null;

				_sync.EnterReadLock();

				try
				{
					res = Storage.Keys;
				}
				finally
				{
					_sync.ExitReadLock();
				}

				return res;
			}
		}

		public bool Remove(TKeyType key)
		{
			var res = false;

			_sync.EnterWriteLock();

			try
			{
				res = Storage.Remove(key);
			}
			finally
			{
				_sync.ExitWriteLock();
			}

			return res;
		}

		public bool TryGetValue(TKeyType key, out TValueType value)
		{
			var res = false;

			_sync.EnterReadLock();

			try
			{
				res = Storage.TryGetValue(key, out value);
			}
			finally
			{
				_sync.ExitReadLock();
			}

			return res;
		}

		public ICollection<TValueType> Values
		{
			get
			{
				ICollection<TValueType> Res = null;

				_sync.EnterReadLock();

				try
				{
					Res = Storage.Values;
				}
				finally
				{
					_sync.ExitReadLock();
				}

				return Res;
			}
		}

		public TValueType this[TKeyType key]
		{
			get
			{
				TValueType res;

				TryGetValue(key, out res);

				return res;
			}
			set
			{
				_sync.EnterWriteLock();

				try
				{
					Storage[key] = value;
				}
				finally
				{
					_sync.ExitWriteLock();
				}
			}
		}

		#endregion

		#region IEnumerable<KeyValuePair<KeyType,ValueType>> Members

		public IEnumerator<KeyValuePair<TKeyType, TValueType>> GetEnumerator()
		{
			IEnumerator<KeyValuePair<TKeyType, TValueType>> Res = null;

			_sync.EnterReadLock();

			try
			{
				var ROD = new Dictionary<TKeyType, TValueType>(Storage);

				Res = ROD.GetEnumerator();
			}
			finally
			{
				_sync.ExitReadLock();
			}

			return Res;
		}

		#endregion

		#region ISimpleDictionary Members

		public object Get(object key)
		{
			return this[(TKeyType)key];
		}

		public void Set(object key, object value)
		{
			this[(TKeyType)key] = (TValueType)value;
		}

		public void Remove(object key)
		{
			Remove((TKeyType)key);
		}

		#endregion

		#region ICollection<KeyValuePair<KeyType,ValueType>> Members

		public void Add(KeyValuePair<TKeyType, TValueType> item)
		{
			Add(item.Key, item.Value);
		}

		public void Clear()
		{
			_sync.EnterWriteLock();

			try
			{
				Storage.Clear();
			}
			finally
			{
				_sync.ExitWriteLock();
			}
		}

		public bool Contains(KeyValuePair<TKeyType, TValueType> item)
		{
			bool Res = false;

			_sync.EnterReadLock();

			try
			{
				Res = Storage.Contains(item);
			}
			finally
			{
				_sync.ExitReadLock();
			}

			return Res;
		}

		public void CopyTo(KeyValuePair<TKeyType, TValueType>[] array, int arrayIndex)
		{
			var Values = new List<KeyValuePair<TKeyType, TValueType>>(this);

			Values.CopyTo(array, arrayIndex);
		}

		public int Count
		{
			get
			{
				int Res = 0;

				_sync.EnterReadLock();

				try
				{
					Res = Storage.Count;
				}
				finally
				{
					_sync.ExitReadLock();
				}

				return Res;
			}
		}

		public bool IsReadOnly
		{
			get { return false; }
		}

		public bool Remove(KeyValuePair<TKeyType, TValueType> item)
		{
			return Remove(item.Key);
		}

		#endregion

		#region IEnumerable Members

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		#endregion
	}

	/// <summary>
	/// Dictionary <typeparamref name="TObjectType"/> to (dictionary <typeparamref name="TKeyType"/> to <typeparamref name="TItemType"/>)
	/// </summary>
	[Serializable]
	[DebuggerStepThrough]
	public class SyncDictionary<TObjectType, TKeyType, TItemType> : SyncDictionary<TObjectType, SyncDictionary<TKeyType, TItemType>>
	{
		public virtual bool SafeExists(TObjectType objectId, TKeyType itemId)
		{
			var res = false;

			SyncDictionary<TKeyType, TItemType> tmp = null;

			TryGetValue(objectId, out tmp);

			if (tmp != null)
				res = tmp.ContainsKey(itemId);

			return res;
		}

		public virtual TItemType SafeGet(TObjectType objectId, TKeyType itemId)
		{
			return SafeGet(objectId, itemId, default(TItemType));
		}

		public virtual TItemType SafeGet(TObjectType objectId, TKeyType itemId, TItemType defaultValue)
		{
			var res = defaultValue;

			SyncDictionary<TKeyType, TItemType> tmp;

			TryGetValue(objectId, out tmp);

			if (tmp != null && !tmp.TryGetValue(itemId, out res))
				res = defaultValue;

			return res;
		}

		public virtual SyncDictionary<TKeyType, TItemType> SafeGet(TObjectType objectId)
		{
			SyncDictionary<TKeyType, TItemType> tmp;

			TryGetValue(objectId, out tmp);

			return tmp;
		}

		public virtual void SafeSet(TObjectType objectId, TKeyType itemId, TItemType item)
		{
			SyncDictionary<TKeyType, TItemType> tmp = null;

			TryGetValue(objectId, out tmp);

			if (tmp == null)
				this[objectId] = tmp = SafeCreateChildDictionary();

			if (tmp != null)
				tmp[itemId] = item;
		}

		public virtual SyncDictionary<TKeyType, TItemType> SafeCreateChildDictionary()
		{
			return new SyncDictionary<TKeyType, TItemType>();
		}

		public virtual void SafeSet(TObjectType objectId, SyncDictionary<TKeyType, TItemType> items)
		{
			this[objectId] = items;
		}

		public virtual void SafeRemove(TObjectType objectId, TKeyType itemId)
		{
			SyncDictionary<TKeyType, TItemType> tmp = null;

			TryGetValue(objectId, out tmp);

			if (tmp == null)
				return;

			tmp.Remove(itemId);

			if (tmp.Count <= 0)
				Remove(objectId);
		}

		/// <summary>
		/// Dictionary <typeparamref name="TObjectType"/> to (dictionary <typeparamref name="TKeyType"/> to <typeparamref name="TItemType"/>)
		/// </summary>
		public SyncDictionary()
		{
		}

		/// <summary>
		/// Dictionary <typeparamref name="TObjectType"/> to (dictionary <typeparamref name="TKeyType"/> to <typeparamref name="TItemType"/>)
		/// </summary>
		public SyncDictionary(IEqualityComparer<TObjectType> comparer)
			: base(comparer)
		{
		}
	}
}